#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <pthread.h>
typedef struct
{
	struct sockaddr_in attr;
	char data[512];
}thread_data;
int server_fd = 0;
void* thread_handle(void* client);
void server_log(struct sockaddr_in* attr);
int main(int argc,char* argv[])
{
	if(argc < 2)
	{
		printf("incorrect parametr\n");
		return 1;
	}
	/*
 	 * 1. 创建socket
 	 * SOCK_DGRAM : 使用UDP传输
 	 */
	server_fd = socket(AF_INET,SOCK_DGRAM,0);
	
	/*
 	 * 2. 设置服务器选项，SO_REUSEADDR，会先解除之前的绑定，在绑定一次，避免重启时因为连接还没有断开导致的bind报错
 	 */
	setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,(void*)NULL,0);
	
	/*
 	 * 3. 设置服务器选项，SO_SNDTIMEO，发送客户端消息的超时时间，避免子线程阻塞
 	 */
	struct timeval time_out = {3,0};
	// setsockopt(server_fd,SOL_SOCKET,SO_RCVTIMEO,(char*)&time_out,sizeof(time_out));
	setsockopt(server_fd,SOL_SOCKET,SO_SNDTIMEO,(char*)&time_out,sizeof(time_out));
	
	/*
 	 * 4. 设置绑定的ip及端口信息
 	 */
	struct sockaddr_in server_attr;
	server_attr.sin_family = AF_INET;
	server_attr.sin_port = htons((short)atoi(argv[1]));
	server_attr.sin_addr.s_addr = INADDR_ANY;
	
	/*
 	 * 5. 绑定本地ip，port
 	 */
	bind(server_fd,(struct sockaddr*)&server_attr,sizeof(server_attr));
	// 存放客户端信息
	thread_data* client;
	// 存放子线程id
	pthread_t tid = 0;
	// 存放子线程属性，用以设置以分离模式启动子线程
	pthread_attr_t thread_attr;
	memset(&thread_attr,0,sizeof(thread_attr));
	pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);
	// 上次是否读取成功的标识，用以判断是否需要重新申请内存
	int recv_flag = 0;
	// 连接信息结构体的大小
	socklen_t addrlen = sizeof(struct sockaddr_in);
	while(1)
	{
		/*
 	 	* 6. 获得客户端发送的数据与客户端信息，用于与客户端通信
 	 	*/
		if(recv_flag == 0)
		{
			client = malloc(sizeof(thread_data));
		}
		memset(client,0,sizeof(struct sockaddr_in));
		if(0 >= recvfrom(server_fd,client->data,sizeof(client->data),0,(struct sockaddr*)&client->attr,&addrlen))
		{
			// 超时或者读取失败则继续下一次等待，设置标识，避免下次重新申请内存
			printf("time out\n");
			recv_flag = 1;
			continue;
		}
		server_log(&client->attr);
		/*
 	 	* 7. 使用子线程处理并发
 	 	*/
		pthread_create(&tid,&thread_attr,thread_handle,(void*)client);
		recv_flag = 0;
	}
	return 0;
}
void* thread_handle(void* client)
{
	thread_data* t = (thread_data*)client;
	char thread_id[20] = {'\0'};
	sprintf(thread_id,"%lu",pthread_self());
	// 连接信息结构体的大小
	strcat(t->data,"\n;thread_id = ");
	strcat(t->data,thread_id);
	sendto(server_fd,t->data,sizeof(t->data),0,(struct sockaddr*)&t->attr,sizeof(t->attr));
	// 释放动态申请的内存
	free(client);
	return NULL;
}
void server_log(struct sockaddr_in* attr)
{
	char ip[16] = {'\0'};
	inet_ntop(AF_INET,&attr->sin_addr.s_addr,ip,sizeof(ip));
	printf("connect from %s handle by %lu\n",ip,pthread_self());
}
